package cn.liwq.demo.future;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.RandomUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

/**
 * @author lweiqiang
 * @date 2023/5/5
 */
@Slf4j
@Service
public class CompletableFutureDemo {
    @SneakyThrows
    public void work() {

        List<String> userList = Arrays.asList("Alice", "Bob", "Charlie", "Charlie", "Charlie9");
        List<CompletableFuture<String>> futures = new ArrayList<>();
        // create a CompletableFuture for each user and add to futures list
        for (String user : userList) {
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(user);
                if (user.equals("Charlie9")) {
                    throw new IllegalArgumentException("handle error");
                }
                return user;
            });
            futures.add(future);
        }
        // wait for all futures to complete
        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).whenComplete((arg, ex) -> {
                if (ex != null) {
                    log.error("exception catch.");
                    futures.forEach(f -> {
                        if (!f.isDone()) {
                            f.cancel(true);
                        }
                    });
                } else {
                    log.info("handler all done");
                }
            }).get(3, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("Exception :{}", e.getMessage(), e);
            throw new RuntimeException(e.getCause().getMessage());
        }
        log.info("done!!!!!!!!");
    }

    public void test2Pool() {
        Executor pool1 = ThreadUtil.newFixedExecutor(15, "pool1-", true);
        Executor pool2 = ThreadUtil.newFixedExecutor(15, "pool2-", true);
        List<String> userList = Lists.newArrayList();
        IntStream.range(0, 20).forEach(i -> userList.add(RandomUtil.randomString(6)));
        List<CompletableFuture<String>> futures = new ArrayList<>();
        // create a CompletableFuture for each user and add to futures list
        for (String user : userList) {
            CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
                log.info("start task ,wait get...");
                try {
                    TimeUnit.SECONDS.sleep(3L);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                try {
                    CompletableFuture.supplyAsync(() -> {
                        log.info("exec get sth");
                        return user;
                    }, pool2).get();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
                return null;
            }, pool1);
            futures.add(future);
        }
        // wait for all futures to complete
        try {
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).whenComplete((arg, ex) -> {
                if (ex != null) {
                    log.error("exception catch.");
                    futures.forEach(f -> {
                        if (!f.isDone()) {
                            f.cancel(true);
                        }
                    });
                } else {
                    log.info("handler all done");
                }
            }).get(30, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("Exception :{}", e.getMessage(), e);
            throw new RuntimeException(e.getCause().getMessage());
        }
        log.info("done!!!!!!!!");
    }

}
